#include "ProcedureCtrl.h"
#include "eutranManager.h"
#include "OamMessageProtocol.h"
#include <algorithm>

ProcedureCtrl::ProcedureCtrl(std::string name):
name_(name),
currentProcIndex_(procedures_.begin())
{
}

bool ProcedureCtrl::start()
{
    currentProcIndex_ = procedures_.begin();

    if( currentProcIndex_==procedures_.end() )
    {
        EMLogError("Empty currentProcIndex_");
        return false;
    }

    EMLogInfo("PROC[%s] start", (*currentProcIndex_)->getName().c_str());
    (*currentProcIndex_)->start();
    
    return true;
}

bool ProcedureCtrl::stop()
{
    return true;
}

ProcedureCtrl& ProcedureCtrl::addProcedure(EmBaseProcedure* proc)
{
    if( proc==NULL )
    {
        EMLogError("NULL pointer");
        return *this;
    }

    procedures_.push_back(proc);
    proc->saveProcedureCtrl(this);

    return *this;
}

void ProcedureCtrl::processMessage(TlvMessage& msg, BbuCtrl& bbu)
{
    if( currentProcIndex_==procedures_.end() )
    {
        EMLogError("procedures_ is finish %d", procedures_.size());
        return;
    }

    EmBaseProcedure* pro = *currentProcIndex_;
    if( pro==NULL )
    {
        EMLogError("NULL pointer");
        return;
    }

    if( RC_PROC_END == pro->processMessage(msg, bbu) )
    {
        currentProcIndex_++;

        if( currentProcIndex_==procedures_.end() )
        {
            // todo
        }
    }
}


EmBaseProcedure::EmBaseProcedure(std::string name):
procName_(name),
procCtrlPtr_(NULL)
{
}

EmBaseProcedure::~EmBaseProcedure()
{
}

void EmBaseProcedure::saveProcedureCtrl(ProcedureCtrl* ctrlPtr)
{
    procCtrlPtr_ = ctrlPtr;
}

std::string EmBaseProcedure::getName()
{
    return procName_;
}

bool EmBaseProcedure::registerMsgHandler(TlvMessage::Protocol_t protocol, TlvMessage::Signal_t signal)
{
    switch(protocol)
    {
        case OamMessageProtocol_Signal::TLV_PROTOCOL_NUMBER:
        {
            break;
        }
        case NmmToX86Protocol_Signal::TLV_PROTOCOL_NUMBER:
        {
            break;
        }
        default:
        {
            EMLogError("Unkown protocol type %d", static_cast<uint32_t>(protocol));
            return false;
        }
    }

    return true;
}


CellConfigProcedure::CellConfigProcedure():
EmBaseProcedure("Cell Config"),
bbu_(NULL)
{
}

CellConfigProcedure::~CellConfigProcedure()
{
}

bool CellConfigProcedure::start()
{
    if( bbu_==NULL )
    {
        EMLogError("NULL pointer");
        return false;
    }
    uint32_t seqNo = bbu_->getInstance().allocSeqNo();
    configMsg_.packSeqNum(seqNo);

    if( !bbu_->getInstance().sendMessage(configMsg_.getMessage()) )
    {
        EMLogError("BBU[%d]: Fail to send message", bbu_->getInstance().getSlotId());
        return false;
    }
    return true;
}

bool CellConfigProcedure::finish()
{
    return true;
}

bool CellConfigProcedure::processMessage(TlvMessage& msg, BbuCtrl&)
{
    if( bbu_==NULL )
    {
        EMLogError("NULL pointer");
        return false;
    }
    NmmToX86Protocol_CellConfigRsp rsp(msg);

    uint8_t rc = 0;
    if( !rsp.unpack(rc) )
    {
        EMLogError("Fail to unpack CellConfigRsp");
        return false;
    }

    EMLogInfo("BBU[%d]: Success to config cell", bbu_->getInstance().getSlotId());

    return true;
}

StateReportProcedure::StateReportProcedure():
EmBaseProcedure("BBU state report procedure"),
isTrgByOam_(false),
bbu_(NULL)
{
}

StateReportProcedure::~StateReportProcedure()
{
}

bool StateReportProcedure::start()
{
    if( bbu_==NULL )
    {
        return false;
    }

    if( bbu_->getInstance().isActive() )
    {
        bbu_->buildAndSendBbuStateReq();
    }
    else
    {
        EMLogInfo("BBU[%d] is inactive", bbu_->getInstance().getSlotId());
        reportState(*bbu_, BBU_INACTIVE);
    }
    return true;
}

bool StateReportProcedure::finish()
{
    EMLogError("Should not invoke");
    return false;
}

bool StateReportProcedure::processMessage(TlvMessage& tlvMsg, BbuCtrl& bbu)
{
    if( tlvMsg.getSignalType()!=OamMessageProtocol_Signal::SYSTEM_STATUS_REPORT )
    {
        EMLogError("Invalid message type[%d]", static_cast<uint32_t>(tlvMsg.getSignalType()));
        return true;
    }

    OamMessageProtocol_SysStatusRsp sysStatusRpt(tlvMsg);
    BbuState bbuState;
    uint32_t stateCode;
    if( !sysStatusRpt.unpack(stateCode, bbuState.stateDesc_, bbuState.cpuTemp_, bbuState.cpuUsage_, bbuState.memUsage_, bbuState.boardTemp_) )
    {
        EMLogError("Fail to unpack SysStatusRsp");

        if( isTrgByOam_ )
        {
            isTrgByOam_ = false;

            EutranMgrPtr->responseFailure();
            return true;
        }
    }
    bbuState.stateCode_ = static_cast<BbuStateCode>(stateCode);
    EMLogInfo("BBU[%d]: SysStatusRsp is %d - %s", bbu.getInstance().getSlotId(), bbuState.stateCode_, bbuState.stateDesc_.c_str());
    EMLogInfo("BBU[%d]: CPU temperature: %d; CPU usage: %d; MEM usage: %d; board temperature: %d", bbu.getInstance().getSlotId(),
               static_cast<uint32_t>(bbuState.cpuTemp_), static_cast<uint32_t>(bbuState.cpuUsage_), static_cast<uint32_t>(bbuState.memUsage_), static_cast<int32_t>(bbuState.boardTemp_));

    if( isTrgByOam_ )
    {
        isTrgByOam_ = false;

        uint32_t paraCode = 0;

        switch(bbuState.stateCode_)
        {
            case BBU_INITIALIZING:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_INITIALIZING;
                break;
            }
            case BBU_SEARCH_CTRL_BD:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_INIT_TO_CTRL_BOARD;
                break;
            }
            case BBU_READY:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_AVAILABLE;
                break;
            }
            case BBU_SNFING:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_SNIFFERING;
                break;
            }
            case BBU_CELL_CFG:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_CELL_CONFIGING;
                break;
            }
            case BBU_LOCATION:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_LOCATION;
                break;
            }
            case BBU_INIT_CFG:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_INIT_CONFIGING;
                break;
            }
            default:
            {
                paraCode = PARA_VALUE_EUTRAN_BBU_INVALID;
                break;
            }
        }

        std::vector < WrapperCmdPara > responseParas;
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_RESULT, static_cast<uint32_t>(PARA_VALUE_RESULT_SUCCESS)));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_BBU_SLOT_ID, static_cast<uint8_t>(bbu.getInstance().getSlotId())));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_BBU_STATE, paraCode));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_BBU_STATE_DES_STR, bbuState.stateDesc_.length(), reinterpret_cast<uint8_t*>(const_cast<char*>(bbuState.stateDesc_.c_str()))));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_MEM_USAGE, bbuState.memUsage_));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_CPU_USAGE, bbuState.cpuUsage_));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_CPU_TEMP, bbuState.cpuTemp_));
        responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_BOARD_TEMP, bbuState.boardTemp_));
        EutranMgrPtr->responseToSrcModule(CMD_GET_RESPONSE| 0x10, responseParas);
    }
    reportState(bbu, bbuState.stateCode_);
    return false;
}

void StateReportProcedure::reportState(BbuCtrl& bbu, BbuStateCode stateCode)
{
    if( bbu.getInstance().getState()==stateCode )
    {
        EMLogInfo("BBU[%d]: state[%d] is not change", bbu.getInstance().getSlotId(), stateCode);
        return;
    }
    EMLogInfo("Report state BBU[%d] to %d", bbu.getInstance().getSlotId(), stateCode);
    std::vector < WrapperCmdPara > responseParas;
    responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_BBU_SLOT_ID, static_cast<uint8_t>(bbu.getInstance().getSlotId())));
    responseParas.push_back(WrapperCmdPara(PARA_TYPE_EUTRAN_BBU_STATE, static_cast<uint32_t>(stateCode)));
    EutranMgrPtr->reportInfo(CMD_EVT_EUTRAN_BBU_STATE, responseParas);

    bbu.getInstance().setState(stateCode);
}

StateReportProcedure* StateReportProcedure::instance()
{
    static StateReportProcedure instance_;
    return &instance_;
}

StateReportProcedure* StateReportProcedure::setBbu(BbuCtrl* ctrl)
{
    if( ctrl==NULL )
    {
        EMLogError("NULL pointer");
        return this;
    }

    bbu_ = ctrl;
    return this;
}

